﻿using Microsoft.Practices.Composite.Events;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using Matchingo.Common.Constants;
using Matchingo.Common.Types.Events;
using Matchingo.Common.Types.Interfaces;
using Matchingo.Common.Types.Types;
using Matchingo.Models.GameLogicProvider;
using Matchingo.Models.ScoreProvider;

namespace GameLogicTestFixtureApp
{
    [TestClass]
    public class GameLogicFixture
    {
        private const int CARD_COUNT = 16;
        private const string PLAYER_NAME = "SmartyP";

        private IHighScoreProvider _scoreProvider;
        private IMemoryGameLogic _gameLogic;
        private IEventAggregator _eventAggregator;

        [TestInitialize]
        public void Setup()
        {
            _scoreProvider = new HighScoreProvider(); // we aren't loading because we want 0 records
            _eventAggregator = new EventAggregator();

            _gameLogic = new MemoryGameLogic(_scoreProvider, _eventAggregator);
            _gameLogic.Initialize();

            Assert.IsTrue(_scoreProvider.GetHighScoresByCardCount(10, CommonConstants.GAME_SETTINGS_DEFAULT_CARDCOUNT).Count == 0); // there should be no game records
        }

        [TestMethod]
        public void ShouldStartNewGame()
        {
            // setup the game we will play
            MatchGameSettings gameSettings = new MatchGameSettings() 
                { 
                    AIGame = false, 
                    CardCount = CARD_COUNT, 
                    PlayerName = PLAYER_NAME 
                };
            
            // start a new game ..
            _gameLogic.StartNewGame(gameSettings);

            // .. and validate some general settings about our new game:
            Assert.IsTrue(_gameLogic.CurrentGame != null); // the current game should not be null            
            Assert.IsTrue(_gameLogic.CurrentGame.CardCount == CARD_COUNT); // our card count should be what we requested
            Assert.IsTrue(_gameLogic.CurrentGame.StartTime == null); // game clock doesnt start til first flip, should be null
            Assert.IsTrue(_gameLogic.CurrentGame.EndTime == null); // the game should not be over, so the end time should be null
            Assert.IsTrue(_gameLogic.CurrentGame.GameTimeElapsed.TotalSeconds < 5); // the game should have started within the last few seconds
            Assert.IsTrue(_gameLogic.CurrentGame.PlayerName == PLAYER_NAME); // the player's name should be what we requested
            Assert.IsTrue(_gameLogic.CurrentGame.Score == 0); // the score should start at zero
            Assert.IsTrue(_gameLogic.CurrentGame.GameState == Game.GameStateEnum.Started); 
            Assert.IsTrue(_gameLogic.CurrentGame.TurnCount == 0); // the turn count should be zero
            Assert.IsFalse(_gameLogic.CurrentGame.ArtificialGame); // this should not be an artificial game
        }

        // with two cards we can quickly test many functions such as flipping cards, finding matches, and ending games
        [TestMethod]
        public void PlayTwoCardGame()
        {
            // setup the game we will play
            MatchGameSettings gameSettings = new MatchGameSettings()
            {
                AIGame = false,
                CardCount = 2,
                PlayerName = PLAYER_NAME
            };

            // start a new game ..
            _gameLogic.StartNewGame(gameSettings);
            Assert.IsTrue(_gameLogic.CurrentGame.GameState == Game.GameStateEnum.Started);
            Assert.IsTrue(_gameLogic.CurrentGame.StartTime == null); // the game clock should not have started yet (first card flip starts it)
            Assert.IsTrue(_gameLogic.CurrentGame.EndTime == null);
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet.Count == 2); // there should be 2 game cards

            // in a 2 card game the two cards should match
            Assert.IsTrue(_gameLogic.CurrentGame.CardsSet[0].MatchingID == _gameLogic.CurrentGame.CardsSet[1].MatchingID);

            // we are going to subscribe to events and keep a count of when they are fired
            int cardsMatched = 0;
            int gameEnded = 0;

            // listen for events
            _eventAggregator.GetEvent<GameEndedEvent>().Subscribe(args => gameEnded++, true);
            _gameLogic.CardsMatched += (e, m) => { cardsMatched++; }; // listen for CardsMatched

            // flip the 1st card
            _gameLogic.Flip(_gameLogic.CurrentGame.CardsSet[0].UniqueID);
            Assert.IsTrue(cardsMatched == 0);
            Assert.IsTrue(gameEnded == 0);
            Assert.IsTrue(_gameLogic.CurrentGame.GameState == Game.GameStateEnum.Started); // game should still be going
            Assert.IsTrue(_gameLogic.CurrentGame.StartTime != null);
            Assert.IsTrue(_gameLogic.CurrentGame.EndTime == null);

			int gameRecordsCount = _scoreProvider.GetHighScoresByCardCount(10, gameSettings.CardCount).Count;	// we want to see if this increments when the game ends

            // flip the 2nd card (which will match)
            _gameLogic.Flip(_gameLogic.CurrentGame.CardsSet[1].UniqueID);
            Assert.IsTrue(cardsMatched == 1);
            Assert.IsTrue(gameEnded == 1);
            Assert.IsTrue(_gameLogic.CurrentGame.GameState == Game.GameStateEnum.Ended);
            Assert.IsTrue(_gameLogic.CurrentGame.StartTime != null);
            Assert.IsTrue(_gameLogic.CurrentGame.EndTime != null);
			Assert.IsTrue(gameRecordsCount + 1 == _scoreProvider.GetHighScoresByCardCount(10, gameSettings.CardCount).Count); // there should be a new game record now that it has ended
        }
    }
}
